import { _decorator, Component, Prefab, instantiate, Node, Vec3, Label } from 'cc';
import { PlayerController } from './PlayerController';//引入自己的模块
const { ccclass, property } = _decorator;//引入注解ccclass和property

//定义枚举类型：赛道格子类型
enum BlockType {
    BT_NONE,//坑
    BT_STONE,//实路
};

//定义枚举类型：游戏状态
enum GameState{
    GS_INIT,//游戏初始化
    GS_PLAYING,//游戏进行中
    GS_END,//游戏结束
};

/*
类中所有的public属性，在Cocos中都可见，需要绑定相应的组件才能生效
private属性在Cocos界面中不可见
*/
@ccclass("GameManager")//定义了Cocos类，继承Component，Component是附加到所有节点的基类
export class GameManager extends Component {

    @property({type: PlayerController})//表示是PlayerController类型的一个属性
    public playerCtrl: PlayerController | null = null;//对应于实体Player

    @property({type: Node})//表示是Node类型的一个属性
    public startMenu: Node | null = null;//对应于实体StartMenu

    // 赛道预制
    @property({type: Prefab})//表示是Prefab类型的一个属性
    public cubePrfb: Prefab | null = null;//对应于Cube.prefab
    // 赛道长度
    @property//表示是typescript内置的属性，不是cocos中定义的
    public roadLength = 50;//道路长度为50
    private _road: BlockType[] = [];//表示每个具体的道路块

    @property({type: Label})//表示是Label类型的一个属性
    public stepsLabel: Label | null = null;//对应于2d的组件stepsLabel

    //游戏开始
    start () {
        this.curState = GameState.GS_INIT;//设置游戏状态，curState通过set关键字声明了set方法
        console.info("开始游戏");//输出游戏开始日志
        this.playerCtrl?.node.on('JumpEnd', this.onPlayerJumpEnd, this);//注册回调函数onPlayJumpEnd，监听JumpEnd事务，该事务传回Player当前位置
    }
    
    //游戏初始化
    init() {
        // 激活主界面，主菜单界面，含有开始按钮
        if (this.startMenu) {
            this.startMenu.active = true;
        }
        // 生成赛道
        this.generateRoad();
        if(this.playerCtrl){
            // 禁止接收用户操作人物移动指令
            this.playerCtrl.setInputActive(false);
            // 重置人物位置，setPosition表示设置本地坐标
            this.playerCtrl.node.setPosition(Vec3.ZERO);
        }
        this.playerCtrl.reset();//重置人物当前位置
    }
    
    //相当于定义了一个curState属性，类型为GameState，此处声明set方法
    set curState (value: GameState) {
        switch(value) {
            case GameState.GS_INIT://游戏初始化
                this.init();//调用init
                break;
            case GameState.GS_PLAYING://游戏进行中
                if (this.startMenu) {//含有开始菜单
                    this.startMenu.active = false;//将开始菜单取消激活
                }
                if (this.stepsLabel) {//含有steps计数label
                    this.stepsLabel.string = '0';//将步数重置为0
                }
                // 设置 active 为 true 时会直接开始监听鼠标事件，此时鼠标抬起事件还未派发
                // 会出现的现象就是，游戏开始的瞬间人物已经开始移动
                // 因此，这里需要做延迟处理
                setTimeout(() => {
                    //此时可以允许用户操作人物移动，但需要延迟处理
                    if (this.playerCtrl) {
                        this.playerCtrl.setInputActive(true);
                    }
                }, 0.1);
                break;
            case GameState.GS_END://游戏结束
                break;
        }
    }

    generateRoad() {
        // 防止游戏重新开始时，赛道还是旧的赛道
        // 因此，需要移除旧赛道，清除旧赛道数据
        this.node.removeAllChildren();//清除旧赛道数据
        this._road = [];
        // 确保游戏运行时，人物一定站在实路上
        this._road.push(BlockType.BT_STONE);//第一格为实路

        // 确定好每一格赛道类型
        for (let i = 1; i < this.roadLength; i++) {
            // 如果上一格赛道是坑，那么这一格一定不能为坑
            if (this._road[i-1] === BlockType.BT_NONE) {
                this._road.push(BlockType.BT_STONE);
            } else {
                this._road.push(Math.floor(Math.random() * 2));//随机生成实路或坑
            }
        }

        // 根据赛道类型生成赛道，根据_road数组生成对应的Node
        for (let j = 0; j < this._road.length; j++) {
            let block: Node = this.spawnBlockByType(this._road[j]);
            // 判断是否生成了道路，因为 spawnBlockByType 有可能返回坑（值为 null）
            if (block) {
                this.node.addChild(block);//添加Node节点
                block.setPosition(j, -1.5, 0);//设置位置
            }
        }
    }

    //根据类型生成道路
    spawnBlockByType(type: BlockType) {
        if (!this.cubePrfb) {
            return null;
        }//如果cubePrfb为空，则直接返回空
        let block: Node | null = null;//定义Node类型的变量
        // 赛道类型为实路才生成
        switch(type) {
            case BlockType.BT_STONE:
                block = instantiate(this.cubePrfb);//从Perfab实例化出新节点
                break;
        }
        return block;
    }

    //点击开始游戏按钮后，设置游戏状态为游戏中
    onStartButtonClicked() {
        this.curState = GameState.GS_PLAYING;
    }

    //判断一次跳跃的结果，落在了什么位置上
    checkResult(moveIndex: number) {
        if (moveIndex < this.roadLength) {
            //跳到了坑上
            if (this._road[moveIndex] == BlockType.BT_NONE) {
                this.curState = GameState.GS_INIT;
            }
        } else {//跳过了最大长度
            this.curState = GameState.GS_INIT;
        }
    }

    //每次人物跳跃结束后，更新计数牌，
    onPlayerJumpEnd(moveIndex: number) {
        if (this.stepsLabel) {
            // 因为在最后一步可能出现步伐大的跳跃，但是此时无论跳跃是步伐大还是步伐小都不应该多增加分数
            this.stepsLabel.string = '' + (moveIndex >= this.roadLength ? this.roadLength : moveIndex);
        }
        this.checkResult(moveIndex);
    }
}